package com.ctsi.commons.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.*;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;

public final class UtilXml {
	private static final Logger log = LoggerFactory.getLogger(UtilXml.class);

	public static Map<String, String> attributesToMap(Node node) {
		Map<String, String> data = new HashMap<String, String>();
		NamedNodeMap attrs = node.getAttributes();
		for (int i = 0; i < attrs.getLength(); i++) {
			data.put(attrs.item(i).getNodeName(), attrs.item(i).getNodeValue());
		}
		return data;
	}

	public static String writeXmlDocument(Document document) throws ClassCastException {
		if (document == null) {

			return null;
		}
		return writeXmlDocument(document.getDocumentElement());
	}

	// public static String writeXmlDocument(Element element) throws
	// java.io.IOException, ClassCastException, ClassNotFoundException,
	// InstantiationException,
	// IllegalAccessException {
	// if (element == null) {
	//
	// return null;
	// }
	//
	// ByteArrayOutputStream bos = new ByteArrayOutputStream();
	// writeXmlDocument(bos, element);
	// String outString = bos.toString("UTF-8");
	//
	// if (bos != null)
	// bos.close();
	// return outString;
	// }

	public static void writeXmlDocument(String filename, Document document) throws java.io.IOException, ClassCastException {
		if (document == null) {
			log.warn("  Document was null, doing nothing");
			return;
		}
		writeXmlDocument(filename, document.getDocumentElement());
	}

	// public static void writeXmlDocument(String filename, Element element)
	// throws java.io.FileNotFoundException, java.io.IOException,
	// ClassCastException,
	// ClassNotFoundException, InstantiationException, IllegalAccessException {
	// if (element == null) {
	// log.warn(" Element was null, doing nothing");
	// return;
	// }
	// if (filename == null) {
	// log.warn(" Filename was null, doing nothing");
	// return;
	// }
	//
	// File outFile = new File(filename);
	// FileOutputStream fos = null;
	// fos = new FileOutputStream(outFile);
	//
	// try {
	// writeXmlDocument(fos, element);
	// } finally {
	// if (fos != null)
	// fos.close();
	// }
	// }
	/**
	 * xml写入stream
	 * 
	 * @param os
	 *            不会被关闭
	 * @param document
	 * @throws java.io.IOException
	 * @throws ClassCastException
	 * @throws ClassNotFoundException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 */
	public static void writeXmlDocument(OutputStream os, Document document) throws ClassCastException {
		if (document == null) {
			log.warn(" Document was null, doing nothing");
			return;
		}
		writeXmlDocument(os, document.getDocumentElement());
	}

	public static String writeXmlDocument(DocumentFragment fragment) throws ClassCastException {
		if (fragment == null) {
			return null;
		}

		ByteOutputStream bos = new ByteOutputStream();
		List<Element> elementList = UtilXml.childElementList(fragment);
		for (Element element : elementList) {
			writeXmlDocument(bos, element);
		}
		String outString = bos.toString(StandardCharsets.UTF_8);

		bos.close();
		return outString;
	}

	/*
	 * public static void writeXmlDocument(OutputStream os, Element element, OutputFormat format) throws java.io.IOException { if (element == null) { log.warn(
	 * " Element was null,doing nothing"); return; } if (os == null) { log.warn(" OutputStream was null, doing nothing"); return; }
	 * 
	 * XMLSerializer serializer = new XMLSerializer(os, format); serializer.asDOMSerializer(); serializer.serialize(element); }
	 */

	/*
	 * public static void writeXmlDocument(OutputStream os, Element element) throws java.io.IOException { OutputFormat format = new
	 * OutputFormat(element.getOwnerDocument()); writeXmlDocument(os, element, format); }
	 */

	// jdk 16 +

	public static void writeXmlDocument(final Node node, final Writer writer, final Charset encoding, boolean enablePrettyPrint) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		// dom l3 jdk15不幸
		DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
		DOMImplementation domImpl = registry.getDOMImplementation("XML 3.0"); //

		DOMImplementationLS domImplLS = (DOMImplementationLS) domImpl.getFeature("LS", "3.0");

		LSSerializer serializer = domImplLS.createLSSerializer();
		serializer.getDomConfig().setParameter("format-pretty-print", enablePrettyPrint);

		LSOutput output = domImplLS.createLSOutput();
		output.setCharacterStream(writer);
		output.setEncoding(encoding.name());

		serializer.write(node, output);
	}

	public static void writeXmlDocument(OutputStream os, Node nodeArg) {
		Transformer transformer = getTransformer("UTF-8", false);
		DOMSource source = new DOMSource(nodeArg);
		StreamResult result = new StreamResult(os);
		try {
			transformer.transform(source, result);
		} catch (TransformerException e) {
			throw new RuntimeException(e);
		}
	}

	public static String writeXmlDocument(Node nodeArg) throws ClassCastException {
		if (nodeArg == null) {

			return null;
		}

		ByteOutputStream bos = new ByteOutputStream();
		writeXmlDocument(bos, nodeArg);
		String outString = bos.toString(StandardCharsets.UTF_8);

		bos.close();
		return outString;
	}

	public static void writeXmlDocument(String filename, Node element) throws java.io.IOException, ClassCastException {
		if (element == null) {
			log.warn(" Element was null, doing nothing");
			return;
		}
		if (filename == null) {
			log.warn("  Filename was null, doing nothing");
			return;
		}

		File outFile = new File(filename);
		OutputStream fos = null;
		fos = new BufferedOutputStream(new FileOutputStream(outFile));

		try {
			writeXmlDocument(fos, element);
		} finally {
			if (fos != null)
				fos.close();
		}
	}

	public static Document readXmlDocument(String content) throws SAXException, ParserConfigurationException, java.io.IOException {
		return readXmlDocument(content, false);
	}

	public static Document readXmlDocument(String content, boolean validate) throws SAXException, ParserConfigurationException, java.io.IOException {
		if (content == null) {
			log.warn("  content was null, doing nothing");
			return null;
		}
		ByteInputStream bis = new ByteInputStream(content.getBytes(StandardCharsets.UTF_8));
		return readXmlDocument(bis, validate, "Internal Content");
	}

	public static Document readXmlDocument(URL url) throws SAXException, ParserConfigurationException, java.io.IOException {
		return readXmlDocument(url, false);
	}

	public static Document readXmlDocument(URL url, boolean validate) throws SAXException, ParserConfigurationException, java.io.IOException {
		if (url == null) {
			log.warn("  URL was null, doing nothing");
			return null;
		}
		InputStream is = url.openStream();
		Document result = readXmlDocument(is, validate, url.toString());
		return result;
	}

	/**
	 * 读取xml
	 * 
	 * @param is
	 *            会被关闭
	 * @return
	 * @throws SAXException
	 * @throws ParserConfigurationException
	 * @throws java.io.IOException
	 */
	public static Document readXmlDocument(InputStream is) throws SAXException, ParserConfigurationException, java.io.IOException {
		return readXmlDocument(is, false, null);
	}

	/**
	 * 读取xml
	 * 
	 * @param is
	 *            会被关闭
	 * @param docDescription
	 * @return
	 * @throws SAXException
	 * @throws ParserConfigurationException
	 * @throws java.io.IOException
	 */
	public static Document readXmlDocument(InputStream is, String docDescription) throws SAXException, ParserConfigurationException, java.io.IOException {
		return readXmlDocument(is, false, docDescription);
	}

	/**
	 * 读取xml
	 * 
	 * @param is
	 *            会被关闭
	 * @param validate
	 * @param docDescription
	 * @return
	 * @throws SAXException
	 * @throws ParserConfigurationException
	 * @throws java.io.IOException
	 */
	public static Document readXmlDocument(InputStream is, boolean validate, String docDescription) throws SAXException, ParserConfigurationException, java.io.IOException {
		if (is == null) {
			log.warn("  InputStream was null,doing nothing");
			return null;
		}

		Document document = null;
		try {
			DocumentBuilder builder = getBuilder(validate, docDescription);

			document = builder.parse(is);
		} finally {
			is.close();
		}

		return document;
	}

	public static DocumentBuilder getBuilder(final boolean validate, String docDescription) throws ParserConfigurationException {

		// final long startTime = System.currentTimeMillis();
		// DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		//
		// log.debug("DocumentBuilderFactory is {}", factory.getClass());
		//
		// factory.setValidating(validate);
		// factory.setNamespaceAware(true);
		//
		// factory.setAttribute("http://xml.org/sax/features/validation", validate);
		// factory.setAttribute("http://apache.org/xml/features/validation/schema", validate);
		//
		// factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", validate);
		// // factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
		// DocumentBuilder builder = factory.newDocumentBuilder();
		// // if (validate) {
		// LocalResolver lr = new LocalResolver(new DefaultHandler());
		// ErrorHandler eh = new LocalErrorHandler(docDescription);
		//
		// builder.setEntityResolver(lr);
		// builder.setErrorHandler(eh);
		// // }
		// double totalSeconds = (System.currentTimeMillis() - startTime) / 1000.0;
		// log.debug("create DocumentBuilder {}s: {}", totalSeconds, docDescription);
		// return builder;
		return getBuilder(null, null, validate, docDescription);
	}

	public static DocumentBuilder getBuilder(EntityResolver lr, ErrorHandler eh, final boolean validate, String docDescription) throws ParserConfigurationException {

		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		log.debug("DocumentBuilderFactory is {}", factory.getClass());
		factory.setNamespaceAware(true);

		factory.setValidating(validate);
		factory.setAttribute("http://xml.org/sax/features/validation", validate);
		factory.setAttribute("http://apache.org/xml/features/validation/schema", validate);

		factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", validate);

		DocumentBuilder builder = factory.newDocumentBuilder();
		if (lr == null)
			lr = new LocalResolver(new DefaultHandler());
		builder.setEntityResolver(lr);

		if (eh != null)
			eh = new LocalErrorHandler(docDescription);
		builder.setErrorHandler(eh);

		return builder;
	}

	/**
	 * 读取文档
	 * 
	 * @param reader
	 *            会被关闭
	 * @param validate
	 * @param docDescription
	 * @return
	 * @throws SAXException
	 * @throws ParserConfigurationException
	 * @throws java.io.IOException
	 */
	public static Document readXmlDocument(Reader reader, boolean validate, String docDescription) throws SAXException, ParserConfigurationException, java.io.IOException {
		if (reader == null) {
			log.warn("  InputStream was null,doing nothing");
			return null;
		}

		Document document = null;
		try {
			DocumentBuilder builder = getBuilder(validate, docDescription);

			document = builder.parse(new InputSource(reader));
		} finally {
			reader.close();
		}

		return document;
	}

	public static Document makeEmptyXmlDocument() {
		return makeEmptyXmlDocument(null);
	}

	public static Document makeEmptyXmlDocument(String rootElementName, String publicid, String systemId) throws ClassCastException, ClassNotFoundException, InstantiationException, IllegalAccessException {
		DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();

		DOMImplementation impl = registry.getDOMImplementation("XML 3.0");
		DocumentType doctype = impl.createDocumentType(rootElementName, publicid, systemId);
		Document document = impl.createDocument(null, null, doctype);
		return document;
	}

	public static Document makeEmptyXmlDocument(String rootElementName) {
		Document document = null;
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

		factory.setValidating(true);

		try {
			DocumentBuilder builder = factory.newDocumentBuilder();

			document = builder.newDocument();
		} catch (Exception e) {
			log.warn("makeEmptyXmlDocument error", e);
		}

		if (document == null)
			return null;

		if (rootElementName != null) {
			Element rootElement = document.createElement(rootElementName);
			document.appendChild(rootElement);
		}

		return document;
	}

	/**
	 * 使用指定名称建立节点
	 * 
	 * @param element
	 *            父节点
	 * @pram childElementName 子节点名称
	 * @param document
	 *            文档
	 */
	public static Element addChildElement(Element element, String childElementName, Document document) {
		Element newElement = document.createElement(childElementName);

		element.appendChild(newElement);
		return newElement;
	}

	/**
	 * 在指定元素下建立新元素，并设置元素的值
	 * 
	 */
	public static Element addChildElementValue(Element element, String childElementName, String childElementValue, Document document) {
		Element newElement = addChildElement(element, childElementName, document);

		newElement.appendChild(document.createTextNode(childElementValue));
		return newElement;
	}

	/**
	 * 建立CDATA数据
	 * 
	 * @param element
	 * @param childElementName
	 * @param childElementValue
	 * @param document
	 * @return
	 */
	public static Element addChildElementCDATAValue(Element element, String childElementName, String childElementValue, Document document) {
		Element newElement = addChildElement(element, childElementName, document);

		newElement.appendChild(document.createCDATASection(childElementValue));
		return newElement;
	}

	/**
	 * 获取子元素
	 * 
	 * @param element
	 * @return
	 */
	public static List<Element> childElementList(Element element) {
		if (element == null)
			return null;

		List<Element> elements = new ArrayList<Element>();
		Node node = element.getFirstChild();

		if (node != null) {
			do {
				if (node.getNodeType() == Node.ELEMENT_NODE) {
					Element childElement = (Element) node;

					elements.add(childElement);
				}
			} while ((node = node.getNextSibling()) != null);
		}
		return elements;
	}

	/**
	 * 返回制定元素的指定名称的子元素
	 * 
	 * @param element
	 * @param childElementName
	 * @return
	 */
	public static List<Element> childElementList(Element element, String childElementName) {
		if (element == null)
			return null;

		List<Element> elements = new ArrayList<Element>();
		Node node = element.getFirstChild();

		if (node != null) {
			do {
				if (node.getNodeType() == Node.ELEMENT_NODE && (childElementName == null || childElementName.equals(node.getNodeName()))) {
					Element childElement = (Element) node;

					elements.add(childElement);
				}
			} while ((node = node.getNextSibling()) != null);
		}
		return elements;
	}

	public static List<Node> childNodeList(Node node) {
		if (node == null)
			return null;

		List<Node> nodes = new ArrayList<Node>();

		node = node.getFirstChild();
		if (node != null) {
			do {
				if (node.getNodeType() == Node.ELEMENT_NODE || node.getNodeType() == Node.COMMENT_NODE) {
					nodes.add(node);
				}
			} while ((node = node.getNextSibling()) != null);
		}
		return nodes;
	}

	/**
	 * 获取指定名称的子元素
	 */
	public static List<Element> childElementList(Element element, Set<String> childElementNames) {
		if (element == null)
			return null;

		List<Element> elements = new ArrayList<Element>();
		if (childElementNames == null)
			return elements;
		Node node = element.getFirstChild();

		if (node != null) {
			do {
				if (node.getNodeType() == Node.ELEMENT_NODE && childElementNames.contains(node.getNodeName())) {
					Element childElement = (Element) node;

					elements.add(childElement);
				}
			} while ((node = node.getNextSibling()) != null);
		}
		return elements;
	}

	public static List<Element> childElementList(DocumentFragment fragment) {
		if (fragment == null)
			return null;
		List<Element> elements = new ArrayList<Element>();
		Node node = fragment.getFirstChild();
		if (node != null) {
			do {
				if (node.getNodeType() == Node.ELEMENT_NODE) {
					Element childElement = (Element) node;
					elements.add(childElement);
				}
			} while ((node = node.getNextSibling()) != null);
		}
		return elements;
	}

	/**
	 * 获取第一指定子元素
	 */
	public static Element firstChildElement(Element element, Set<String> childElementNames) {
		if (element == null)
			return null;
		// get the first element with the given name
		Node node = element.getFirstChild();

		if (node != null) {
			do {
				if (node.getNodeType() == Node.ELEMENT_NODE && childElementNames.contains(node.getNodeName())) {
					Element childElement = (Element) node;

					return childElement;
				}
			} while ((node = node.getNextSibling()) != null);
		}
		return null;
	}

	/**
	 * 返回第一个子元素
	 */
	public static Element firstChildElement(Element element) {
		if (element == null)
			return null;
		// get the first element with the given name
		Node node = element.getFirstChild();

		if (node != null) {
			do {
				if (node.getNodeType() == Node.ELEMENT_NODE) {
					Element childElement = (Element) node;

					return childElement;
				}
			} while ((node = node.getNextSibling()) != null);
		}
		return null;
	}

	/**
	 * 为元素增加一个属性，并负值
	 * 
	 * @param parent
	 * @param attributeName
	 * @param value
	 */
	public static void addAttribute(Element parent, String attributeName, String value) {
		Attr node = parent.getOwnerDocument().createAttribute(attributeName);
		node.setValue(value);
		parent.setAttributeNode(node);
	}

	/**
	 * 获取指定节点的第一个子节点
	 * 
	 */
	public static Element firstChildElement(Element element, String childElementName) {
		if (element == null)
			return null;

		Node node = element.getFirstChild();

		if (node != null) {
			do {
				if (node.getNodeType() == Node.ELEMENT_NODE && (childElementName == null || childElementName.equals(node.getNodeName()))) {
					Element childElement = (Element) node;

					return childElement;
				}
			} while ((node = node.getNextSibling()) != null);
		}
		return null;
	}

	/**
	 * 返回指定节点的属性名称为attrName value 为指定值得子节点 返回第一个
	 */
	public static Element firstChildElement(Element element, String childElementName, String attrName, String attrValue) {
		if (element == null)
			return null;

		Node node = element.getFirstChild();

		if (node != null) {
			do {
				if (node.getNodeType() == Node.ELEMENT_NODE && (childElementName == null || childElementName.equals(node.getNodeName()))) {
					Element childElement = (Element) node;

					String value = childElement.getAttribute(attrName);

					if (value != null && value.equals(attrValue)) {
						return childElement;
					}
				}
			} while ((node = node.getNextSibling()) != null);
		}
		return null;
	}

	/** 获取子元素值. */
	public static String childElementValue(Element element, String childElementName) {
		if (element == null)
			return null;
		// get the value of the first element with the given name
		Element childElement = firstChildElement(element, childElementName);

		return elementValue(childElement);
	}

	/**
	 * 获取子元素值 ，可指定默认
	 */
	public static String childElementValue(Element element, String childElementName, String defaultValue) {
		if (element == null)
			return defaultValue;
		// get the value of the first element with the given name
		Element childElement = firstChildElement(element, childElementName);
		String elementValue = elementValue(childElement);

		if (elementValue == null || elementValue.length() == 0)
			return defaultValue;
		else
			return elementValue;
	}

	/**
	 * 获取元素的值
	 * 
	 * @param element
	 * @return
	 */
	public static String elementValue(Element element) {
		if (element == null)
			return null;
		element.normalize();
		Node textNode = element.getFirstChild();

		if (textNode == null)
			return null;

		StringBuilder valueBuffer = new StringBuilder(128);
		do {
			if (textNode.getNodeType() == Node.CDATA_SECTION_NODE || textNode.getNodeType() == Node.TEXT_NODE) {
				valueBuffer.append(textNode.getNodeValue());
			}
		} while ((textNode = textNode.getNextSibling()) != null);
		return valueBuffer.toString();
		// return nodeValue(element);
	}

	public static void parseTxt(Node node, Appendable valueBuffer) throws DOMException, IOException {

		node.normalize();
		Node textNode = node.getFirstChild();

		if (textNode != null) {
			do {
				if (textNode.getNodeType() == Node.CDATA_SECTION_NODE || textNode.getNodeType() == Node.TEXT_NODE) {
					valueBuffer.append(textNode.getNodeValue());
				} else {
					parseTxt(textNode, valueBuffer);
				}
			} while ((textNode = textNode.getNextSibling()) != null);
		}

	}

	public static String nodeValue(Node node) {
		if (node == null)
			return null;
		// make sure we get all the text there...
		node.normalize();
		Node textNode = node.getFirstChild();

		if (textNode == null)
			return null;

		StringBuilder valueBuffer = new StringBuilder(128);
		do {
			if (textNode.getNodeType() == Node.CDATA_SECTION_NODE || textNode.getNodeType() == Node.TEXT_NODE) {// || textNode.getNodeType() ==
																												// Node.COMMENT_NODE
				valueBuffer.append(textNode.getNodeValue());
			}
		} while ((textNode = textNode.getNextSibling()) != null);
		return valueBuffer.toString();
	}

	public static Transformer getTransformer(String encoding, boolean omitXMLDeclaration) {
		TransformerFactory tFactory = TransformerFactory.newInstance();
		Transformer transformer;
		try {
			transformer = tFactory.newTransformer();
		} catch (TransformerConfigurationException e) {
			throw new RuntimeException(e);
		}
		transformer.setOutputProperty("indent", "yes");
		transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
		transformer.setOutputProperty("encoding", encoding);
		if (omitXMLDeclaration) {
			transformer.setOutputProperty("omit-xml-declaration", "yes");
		}
		return transformer;
	}

	/**
	 * 获取指定node下的xml
	 * 
	 * @param node
	 * @param omitXMLDeclaration
	 *            是否删除xml标记
	 * @return
	 */
	public static String getContent(Node node, boolean omitXMLDeclaration) {
		try {
			ByteOutputStream baos = new ByteOutputStream();

			Transformer transformer = getTransformer(UtilFile.Charset_UTF8, omitXMLDeclaration);

			DOMSource source = new DOMSource(node);
			StreamResult result = new StreamResult(baos);
			transformer.transform(source, result);

			String cont = baos.toString(StandardCharsets.UTF_8);

			baos.close();
			return cont;
		} catch (Exception ex) {
			log.error("getContent error", ex);
			throw new RuntimeException(ex);
		}
	}

	/**
	 * xsd定位
	 * 
	 * @author yueming
	 * 
	 */
	public static class LocalResolver implements EntityResolver {

		private boolean hasDTD = false;

		private final EntityResolver defaultResolver;

		public LocalResolver(EntityResolver defaultResolver) {
			this.defaultResolver = defaultResolver;
		}

		/**
		 * 处理dtd或者schema文件位置
		 * 
		 * 
		 * @param publicId
		 *            - Public ID of DTD
		 * @param systemId
		 *            - System ID of DTD
		 * @return InputSource of DTD
		 */
		@Override
		public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {

			hasDTD = false;
			log.debug("publicId :{},   systemId :{}", publicId, systemId);
			// 处理schema,查询scheman位置
			int lastSlash = systemId.lastIndexOf("/");
			int count = 0;
			for (int i = 0; i < systemId.length(); i++) {
				if (systemId.charAt(i) == '/')
					count++;
			}

			URL resourceUrl = null;
			String filename = null;

			if (count > 3) {
				// 处理带有目录的格式http://ibatis.apache.org/dtd/sql-map-2.dtd
				// http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd 不行
				String temStr = systemId.substring(0, lastSlash);
				int secondLast = temStr.lastIndexOf("/");

				if (secondLast != -1) {
					filename = systemId.substring(secondLast + 1);
					log.debug("load dtd from :{}", filename);
					resourceUrl = UtilURL.fromResource(filename);

				}
			}

			if (resourceUrl == null) {
				if (lastSlash == -1) {
					filename = systemId;
					log.debug("load dtd from :{}", filename);
					resourceUrl = UtilURL.fromResource(filename);
				} else {
					filename = systemId.substring(lastSlash + 1);
					log.debug("load dtd from :{}", filename);
					resourceUrl = UtilURL.fromResource(filename);
				}

			}
			if (resourceUrl == null) {
				if (lastSlash == -1) {
					filename = systemId;
				} else {
					filename = systemId.substring(lastSlash + 1);
				}
				log.info("load dtd from :dtd/{}", filename);
				resourceUrl = UtilURL.fromResource("dtd/" + filename);
			}
			if (resourceUrl != null) {
				log.debug("local dtd   from :{}", resourceUrl);
				InputStream resStream = resourceUrl.openStream();
				InputSource inputSource = new InputSource(resStream);

				if (UtilValidate.isNotEmpty(publicId)) {
					inputSource.setPublicId(publicId);
				}
				hasDTD = true;

				return inputSource;
			} else {
				log.warn("local dtd not found from :{}", systemId);
				// 好像有时候会在网站下载??
				return defaultResolver.resolveEntity(publicId, systemId);
				// return null;
			}

		}

		/**
		 * 是否有本地验证文件
		 * 
		 * @return boolean - true 有
		 */
		public boolean hasDTD() {
			return hasDTD;
		}
	}

	/**
	 * xml校验 打印错误信息
	 */
	public static class LocalErrorHandler implements ErrorHandler {

		private final String docDescription;

		public LocalErrorHandler(String docDescription) {
			this.docDescription = docDescription;

		}

		@Override
		public void error(SAXParseException exception) {

			log.error("XmlFileLoader: File {} process error. Line: {}. Error message: " + exception.getMessage(), docDescription, String.valueOf(exception.getLineNumber()));

		}

		@Override
		public void fatalError(SAXParseException exception) {

			log.error("XmlFileLoader: File {} process error. Line: {}. Error message: " + exception.getMessage(), docDescription, String.valueOf(exception.getLineNumber()));

		}

		@Override
		public void warning(SAXParseException exception) {

			log.error("XmlFileLoader: File {} process error. Line: {}. Error message: " + exception.getMessage(), docDescription, String.valueOf(exception.getLineNumber()));

		}
	}

	public static void writeXmlDocument(Node newNode, Node nodeArg) {
		Transformer transformer = getTransformer(UtilFile.Charset_UTF8, false);
		DOMSource source = new DOMSource(nodeArg);
		Result result = new DOMResult(newNode);
		try {
			transformer.transform(source, result);
		} catch (TransformerException e) {
			throw new RuntimeException(e);
		}
	}

	/** 增加具有名称空间的元素. */
	public static Element addChildElementNSElement(Element element, String childElementName, Document document, String nameSpaceUrl) {
		Element newElement = document.createElementNS(nameSpaceUrl, childElementName);
		element.appendChild(newElement);
		return element;
	}

	/**
	 * 设置自元素值 支持名称空间
	 */
	public static Element addChildElementNSValue(Element element, String childElementName, String childElementValue, Document document, String nameSpaceUrl) {
		Element newElement = document.createElementNS(nameSpaceUrl, childElementName);
		newElement.appendChild(document.createTextNode(childElementValue));
		element.appendChild(newElement);
		return element;
	}

	/**
	 * 建立 <code>LSOutput</code> 实例.
	 * 
	 * @param impl
	 *            A <code>DOMImplementationLS</code> instance
	 * @param os
	 *            输出目标
	 * @param encoding 编码,默认
	 *            UTF-8
	 * @return A <code>LSOutput</code> instance
	 * @see <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/">DOM Level 3 Load and Save Specification</a>
	 */
	public static LSOutput createLSOutput(DOMImplementationLS impl, OutputStream os, String encoding) {
		LSOutput out = impl.createLSOutput();
		if (os != null) {
			out.setByteStream(os);
		}
		if (encoding != null) {
			out.setEncoding(encoding);
		}
		return out;
	}

	/**
	 * 获取 a <code>LSSerializer</code> 实例.
	 * 
	 * @param impl
	 *            A <code>DOMImplementationLS</code> instance
	 * @param includeXmlDeclaration 是否包括xml声明
	 * 
	 * @param enablePrettyPrint 是否格式化
	 * 
	 * @return A <code>LSSerializer</code> instance
	 * @see <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/">DOM Level 3 Load and Save Specification</a>
	 */
	public static LSSerializer createLSSerializer(DOMImplementationLS impl, boolean includeXmlDeclaration, boolean enablePrettyPrint) {
		LSSerializer writer = impl.createLSSerializer();
		DOMConfiguration domConfig = writer.getDomConfig();
		domConfig.setParameter("xml-declaration", includeXmlDeclaration);
		domConfig.setParameter("format-pretty-print", enablePrettyPrint);
		return writer;
	}

	/**
	 * 变换xml
	 * 
	 * @param transformer
	 *            A <code>Transformer</code> instance
	 * @param node
	 *            The <code>Node</code> to serialize
	 * @param os
	 *            The <code>OutputStream</code> to serialize to
	 * @see <a href="http://java.sun.com/javase/6/docs/api/javax/xml/transform/package-summary.html">JAXP TrAX</a>
	 * @throws TransformerException
	 */
	public static void transformDomDocument(Transformer transformer, Node node, OutputStream os) throws TransformerException {
		DOMSource source = new DOMSource(node);
		StreamResult result = new StreamResult(os);
		transformer.transform(source, result);
	}

	public static final List<String> childElementValues(Element element, String childElementName) {
		if (element == null)
			return null;

		List<String> values = new ArrayList<String>();
		Node node = element.getFirstChild();

		if (node != null) {
			do {
				if (node.getNodeType() == Node.ELEMENT_NODE && (childElementName == null || childElementName.equals(node.getNodeName()))) {
					Element childElement = (Element) node;

					values.add(elementValue(childElement));
				}
			} while ((node = node.getNextSibling()) != null);
		}
		return values;
	}

	/**
	 * 无前缀node name
	 * 
	 * @param node
	 * @return
	 */
	public static String getNodeNameIgnorePrefix(Node node) {
		if (node == null)
			return null;
		String nodeName = node.getNodeName();
		if (nodeName.contains(":")) {

			nodeName = nodeName.split(":")[1];
		}
		return nodeName;
	}

	/**
	 * 获取没有前缀的tag名称
	 * 
	 * @param element
	 * @return
	 */
	public static String getTagNameIgnorePrefix(Element element) {
		if (element == null)
			return null;
		String tagName = element.getTagName();
		if (tagName.contains(":")) {
			// remove any possible prefix
			tagName = tagName.split(":")[1];
		}
		return tagName;
	}

	/**
	 * 获取忽略前缀属性
	 * 
	 * @param element
     * @param attributeName
	 * @return
	 */
	public static String getAttributeValueIgnorePrefix(Element element, String attributeName) {
		if (element == null)
			return "";

		NamedNodeMap attributes = element.getAttributes();
		if (attributes != null) {
			for (int i = 0, size = attributes.getLength(); i < size; i++) {
				Node node = attributes.item(i);
				if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
					String nodeName = getNodeNameIgnorePrefix(node);
					if (nodeName.equals(attributeName)) {
						return node.getNodeValue();
					}
				}
			}
		}
		return "";
	}
}
